package com.fdb.efp.nls.service.impl.sed.repay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fdb.efp.nls.service.facade.NlsQueueSoltComnService;
import com.fdb.efp.nls.service.facade.queue.NlsProcessBizService;
import com.fdb.efp.nls.service.facade.queue.NlsProcessRuleService;
import com.fdb.efp.nls.service.facade.sed.RepaySoltService;
import com.fdb.efp.nls.service.vo.queue.NlsProcessBizVO;
import com.fdb.efp.nls.service.vo.queue.NlsProcessRuleVO;
import com.fdb.basic.framework.core.exception.BizException;
import com.fdb.basic.framework.core.exception.ExternalSysException;
import com.fdb.basic.framework.core.exception.OcmSystemException;
import com.fdb.basic.framework.core.util.DateUtility;
import com.fdb.efp.basic.framework.mq.client.producer.MQProducerMessageClient;
import com.fdb.efp.basic.framework.mq.constant.TagEnums;
import com.fdb.efp.basic.framework.mq.constant.TopicEnums;
import com.fdb.efp.esb.common.constant.EsbBizEnums;
import com.fdb.efp.nls.service.facade.LoanRepayDetailService;
import com.fdb.efp.loan.service.vo.LoanRepayDetailVO;
import com.fdb.efp.nls.common.constant.NlsApplyInfoEnums;
import com.fdb.efp.nls.common.constant.NlsProcessBizEnums;
import com.fdb.efp.nls.common.constant.PrdAdaptedSoltEnums;
import com.fdb.efp.nls.common.constant.QueueTaskEnums;
import com.fdb.efp.nls.common.constant.sed.SedNlsEnum.ExceptionLocation;
import com.fdb.efp.nls.common.exception.LoanRepaySyncException;
import com.fdb.efp.nls.common.exception.RepayException;
import com.fdb.efp.nls.service.impl.LoanReleaseOrRepayQueueHangHandle;
import org.apache.rocketmq.common.message.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.reflect.Method;

import static com.fdb.basic.framework.core.constant.BaseConstant.RY_STS_1;
import static com.fdb.efp.nls.common.constant.psd.NlsReplyFlowStatus.FLOW_STATUS_5;

/**
* 普税贷产品对税e贷产品的此部分代码做了复用
* 说明：为了保证功能的复用性、提升开发的工作效率，对该代码进行了复用
* 评估是否会影响线上功能：否
* 评估时间：2020/5/25
* 处理方案：保留复用
*/
/**
 * 需求编号：【2019D0519】（业务提的需求编号）
 * 问题编号：【xxxxxxx】（若不是因为修复问题而新增该类，则留空）
 * 开发人员：qiuyf
 * 创建日期：2019年5月29日 上午10:57:56
 * 功能描述：还款队列插槽服务实现类
 */
public abstract class RepaySoltServiceImpl implements RepaySoltService {

	private static Logger logger = LoggerFactory.getLogger(RepaySoltServiceImpl.class);

	@Override
	public void execute(NlsProcessBizVO nlsProcessBizVo, LoanRepayDetailVO loanRepayDetailVO, int soltOrder) throws Exception {
		// 预处理
		String applySeq = nlsProcessBizVo.getApplySeq();
		String soltCode = nlsProcessBizVo.getSoltCode();
		String soltName = nlsProcessBizVo.getSoltName();
		String soltService = nlsProcessBizVo.getSoltService();
		logger.info("*********************************************");
		logger.info("*            开始一个新的插槽                 * ");
		logger.info("* 申请流水号 : 【" + applySeq + "】  *");
		logger.info("* 借据号 : 【" + loanRepayDetailVO.getLoanNo() + "】  *");
		logger.info("* 客户名称 : 【" + loanRepayDetailVO.getCustName() + "】  *");
		logger.info("* 全局流水号 : 【" + loanRepayDetailVO.getGlobalSerno() + "】  *");
		logger.info("* 插槽代码 : 【" + soltCode + "】  *");
		logger.info("* 插槽名称 : 【" + soltName + "】  *");
		logger.info("* 插槽服务 : 【" + soltService + "】  *");
		logger.info("* 创建日期 : 【" + loanRepayDetailVO.getSetlCreateDt() + "】*");
		logger.info("*********************************************");
		try {
			// 根据插槽代码 映射
			Method methodSoltstate = nlsProcessBizVo.getClass().getMethod("setSoltState" + soltOrder, String.class);
			// 更新该插槽状态为执行中
			logger.info("申请流水号 : 【" + applySeq + "】  更新该插槽状态为执行中开始");
			methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.EXECUTING.getCode());
			nlsProcessBizService.updateByPk(nlsProcessBizVo);
			logger.info("申请流水号 : 【" + applySeq + "】  更新该插槽状态为执行中结束");
			logger.info("申请流水号 : 【" + applySeq + "】  调用该插槽的业务逻辑处理开始");
			this.doExecute(nlsProcessBizVo, loanRepayDetailVO);
			logger.info("申请流水号 : 【" + applySeq + "】  调用该插槽的业务逻辑处理结束");
			boolean exceptionFlag = nlsProcessBizVo.isExceptionFlag();
			int hangTimes = nlsProcessBizVo.getHangTimes();
			logger.info("申请流水号 : 【" + applySeq + "】  根据异常标志【" + exceptionFlag + "】处理后续网贷任务队列信息");
			// 异常处理，执行以上服务完成后更新【 网贷业务过程 】中对应的插槽状态
			if (exceptionFlag) {
				Exception exception = nlsProcessBizVo.getExceptionMsg();
				logger.info("申请流水号 : 【" + applySeq + "】 根据异常【" + exception + "】处理后续网贷任务队列信息");

				//查询规则过程信息 
				NlsProcessRuleVO nlsProcessRuleVO = new NlsProcessRuleVO();
				nlsProcessRuleVO.setApplySeq(applySeq);
				nlsProcessRuleVO = nlsProcessRuleService.queryByPk(nlsProcessRuleVO);
				if (exception instanceof ExternalSysException) {
					logger.info("申请流水号 : 【" + applySeq + "】 处理该插槽的外部系统异常开始");
					// 每次轮询队列任务表中外部系统处理任务后再次触发外部系统异常后，对异常处理次数进行控制，如果大于等于三次直接失败
					hangTimes = hangTimes + 1;
					if (hangTimes > QueueTaskEnums.QueueHangTimesEnum.MAX.getTimes()) {
						String message = "申请流水号 : 【" + applySeq + "】 异常类型为外部系统类型时，该插槽异常处理次数为：【" + hangTimes + "】，更新该插槽状态为失败，更新网贷任务队列中队列任务状态为执行失败， "
								+ "更新还款申请审批状态为拒绝";
						methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.FAILURE.getCode());
						nlsProcessBizVo.setHangTimes(hangTimes);
						nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.FAILURE.getCode());
						//设置插槽结果信息 
						loanRepayDetailVO.setSoltResultDetails(JSONObject.toJSONString(nlsProcessBizVo));
						//设置规则结果信息 
						loanRepayDetailVO.setRuleResultDetails(JSONObject.toJSONString(nlsProcessRuleVO));
						//设置结果码 
						loanRepayDetailVO.setRspCode(NlsProcessBizEnums.RspInfoEnum.DEAL_MAX_TIMES.getRspCode());
						//设置结果信息 
						loanRepayDetailVO.setRspMsg(NlsProcessBizEnums.RspInfoEnum.DEAL_MAX_TIMES.getRspMsg());
						//设置拒绝原因 
						loanRepayDetailVO.setRefuseCause(exception.getMessage());
						if(ExceptionLocation.SYN_INFO_AFTER_REPAY.getValue().equals(((ExternalSysException) exception).getErrorCode())) {
							logger.error(message);
							message = exception.getMessage();
							//还原审批状态为“通过”
							loanRepayDetailVO.setWfApprSts(NlsApplyInfoEnums.NlsApplyState.PASS.getKey());
						}else {
							logger.info(message + "开始");
							//设置审批状态信息为失败 
							loanRepayDetailVO.setWfApprSts(NlsApplyInfoEnums.NlsApplyState.REJECT.getKey());
							logger.info(message + "结束");
						}
						// 发送告警 @date 2019/06/19
						nlsQueueSoltComnService.alarmNotice(EsbBizEnums.AlarmObjectName.OCM_REAL_TIME.getValue(), EsbBizEnums.AlarmLvl.SERIOUS.getValue(), message);
					} else {
						logger.info("申请流水号 : 【" + applySeq + "】异常类型为外部系统异常时更新该插槽状态为挂起，更新网贷任务队列中队列任务状态为挂起，更新还款申请审批状态为挂起");
						methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.HANG.getCode());
						nlsProcessBizVo.setHangTimes(hangTimes);
						nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.HANG.getCode());
						//设置插槽结果信息 
						loanRepayDetailVO.setSoltResultDetails(JSONObject.toJSONString(nlsProcessBizVo));
						//设置规则结果信息 
						loanRepayDetailVO.setRuleResultDetails(JSONObject.toJSONString(nlsProcessRuleVO));
						//设置结果码 
						loanRepayDetailVO.setRspCode(NlsProcessBizEnums.RspInfoEnum.HANG.getRspCode());
						//设置结果信息 
						loanRepayDetailVO.setRspMsg(NlsProcessBizEnums.RspInfoEnum.HANG.getRspMsg());
						//设置审批状态信息为挂起 
						loanRepayDetailVO.setWfApprSts(NlsApplyInfoEnums.NlsApplyState.HANG.getKey());
						//设置拒绝原因 
						loanRepayDetailVO.setRefuseCause(exception.getMessage());
						//最近修改时间 
						nlsProcessBizVo.setLastModifyTime(DateUtility.getCurrAppDateTimeString());
						loanRepayDetailVO.setLastChgDt(DateUtility.getCurrAppDateTimeString());

						nlsProcessBizService.updateByPk(nlsProcessBizVo);

						/**
						 * mycat中不允许修改作为分库依据的列，所以更新的时候去掉“loan_no”
						 * 将更新执行的bean中loanNo字段赋值为null，更新语句会检测是否为空，再做更新。
						 * @date 2019/06/15
						 * @author qiuyf
						 * 
						 * 【2019/06/17，需求要求不使用mycat分片】
						 * loanRepayDetailVO.setLoanNo(null);
						 */

						loanRepayDetailService.updateByPk(loanRepayDetailVO);
						logger.info("申请流水号 : 【" + applySeq + "】处理该插槽的挂起异常结束");

						/**
						 * 【变更原因】前面为了更新loanRepayDetailVO成功，将loanRepayDetailVO里面字段loanNo设置为空了
						 * 【变更操作】在发起挂起消息前，给loanNo重新赋值
						 * @date 2019/06/15
						 * @author qiuyf
						 * 
						 * 【2019/06/17，需求要求不使用mycat分片】
						 * loanRepayDetailVO.setLoanNo(loanNo);
						 */
						// 设置异常信息
						loanRepayDetailVO.setExceptionMsg(exception.getMessage());
						// 延迟3s重新发送消息，主题是“挂起”
						producerService.sendMsg(new Message(TopicEnums.REPAY_QUEUE_TASK_HANG_TOPIC.getTopicId(), TagEnums.REPAY_TAG_QUEUE_HANG_TASK.getTagId(),
								loanRepayDetailVO.getSetlSeq().concat(".").concat(String.valueOf(hangTimes)),
								JSON.toJSONString(loanRepayDetailVO).getBytes("UTF-8")), 3);
						throw exception;
					}
				} else if (exception instanceof OcmSystemException) {
					logger.info("申请流水号 ：【" + applySeq + "】 处理该插槽的系统内部异常开始");
					hangTimes = hangTimes + 1;
					if (hangTimes > QueueTaskEnums.QueueHangTimesEnum.MAX.getTimes()) {
						logger.error("申请流水号【" + applySeq + "】 异常类型为系统内部异常类型时，该插槽异常处理次数为：【" + hangTimes + "】，已超过最大容忍值！");
						nlsProcessBizVo.setHangTimes(hangTimes);
						if(ExceptionLocation.SYN_INFO_AFTER_REPAY.getValue().equals(((OcmSystemException) exception).getErrorCode()) ||
								ExceptionLocation.INSERT_LOAN_PLAN_AFTER_REPAY.getValue().equals(((OcmSystemException) exception).getErrorCode())) {
							logger.info("申请流水号 : 【" + applySeq + "】，当执行借据信息、还款计划信息更新插槽时，因系统内部异常重试了三次后，更新该插槽状态为失败，队列任务状态为执行失败");
							methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.FAILURE.getCode());
							nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.FAILURE.getCode());
						}
						nlsProcessBizVo.setLastModifyTime(DateUtility.getCurrAppDateTimeString());
						nlsProcessBizService.updateByPk(nlsProcessBizVo);
						
						// 发送告警 @date 2019/06/19
						nlsQueueSoltComnService.alarmNotice(EsbBizEnums.AlarmObjectName.OCM_REAL_TIME.getValue(), EsbBizEnums.AlarmLvl.SERIOUS.getValue(), exception.getMessage());

						throw exception;
					} else {
						logger.info("申请流水号 ：【" + applySeq + "】异常类型为内部系统异常时，更新该插槽状态为挂起");
						methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.HANG.getCode());
						nlsProcessBizVo.setHangTimes(hangTimes);
						//最近修改时间 
						nlsProcessBizVo.setLastModifyTime(DateUtility.getCurrAppDateTimeString());

						nlsProcessBizService.updateByPk(nlsProcessBizVo);

						//设置异常出现标识 @date 2019/06/27
						loanRepayDetailVO.setExceptionLocation(((OcmSystemException) exception).getErrorCode());

						// 延迟3s重新发送消息，主题是“挂起”
						producerService.sendMsg(new Message(TopicEnums.REPAY_QUEUE_TASK_HANG_TOPIC.getTopicId(), TagEnums.REPAY_TAG_QUEUE_HANG_TASK.getTagId(),
								loanRepayDetailVO.getSetlSeq().concat(".").concat(String.valueOf(hangTimes)),
								JSON.toJSONString(loanRepayDetailVO).getBytes("UTF-8")), 3);
						throw exception;
					}
				} else if (exception instanceof BizException) {
					logger.info("申请流水号：【" + applySeq + "】处理该插槽的业务异常开始，更新该插槽状态为失败， 更新网贷任务队列中队列任务状态为执行失败和更新还款申请审批状态为拒绝");
					methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.FAILURE.getCode());
					nlsProcessBizVo.setHangTimes(hangTimes);
					nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.FAILURE.getCode());
					//设置插槽结果信息 
					loanRepayDetailVO.setSoltResultDetails(JSONObject.toJSONString(nlsProcessBizVo));
					//设置规则结果信息 
					loanRepayDetailVO.setRuleResultDetails(JSONObject.toJSONString(nlsProcessRuleVO));
					//设置结果码 
					loanRepayDetailVO.setRspCode(NlsProcessBizEnums.RspInfoEnum.FAILURE.getRspCode());
					//设置结果信息 
					loanRepayDetailVO.setRspMsg(NlsProcessBizEnums.RspInfoEnum.FAILURE.getRspMsg());
					//设置审批状态信息为失败 
					loanRepayDetailVO.setWfApprSts(NlsApplyInfoEnums.NlsApplyState.REJECT.getKey());
					//设置拒绝原因 
					loanRepayDetailVO.setRefuseCause(exception.getMessage());
					logger.info("申请流水号 : 【" + applySeq + "】处理该插槽的业务异常结束");
				}else if (exception instanceof RepayException) {
					logger.info("申请流水号 : 【" + applySeq + "还款服务执行失败,转为再还款补偿任务中执行");
					methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.SUCCESS.getCode());
					nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.SUCCESS.getCode());
					//设置插槽结果信息
					loanRepayDetailVO.setSoltResultDetails(JSONObject.toJSONString(nlsProcessBizVo));
					//设置规则结果信息
					loanRepayDetailVO.setRuleResultDetails(JSONObject.toJSONString(nlsProcessRuleVO));
					//设置结果码
					loanRepayDetailVO.setRspCode(NlsProcessBizEnums.RspInfoEnum.SUCCESS.getRspCode());
					//设置结果信息
					loanRepayDetailVO.setRspMsg(NlsProcessBizEnums.RspInfoEnum.SUCCESS.getRspMsg());
					//设置审批状态信息为处理中
					loanRepayDetailVO.setWfApprSts(NlsApplyInfoEnums.NlsApplyState.DEALING.getKey());
					// 设置执行次数
					loanRepayDetailVO.setExecuteTime(0);
					//设置拒绝原因
					loanRepayDetailVO.setRefuseCause(exception.getMessage());
					// 设置流程状态
					loanRepayDetailVO.setFlowStatus(FLOW_STATUS_5);

					// 发送信息
					producerService.sendMsg(new Message(TopicEnums.LOANRELEASEORREPAY_QUEUE_TASK_TOPIC.getTopicId(), TagEnums.LOANRELEASEORREPAY_QUEUE_TASK.getTagId(),
							loanRepayDetailVO.getSetlSeq().concat(".").concat(LoanReleaseOrRepayQueueHangHandle.LOANREPAYDETAILVO+loanRepayDetailVO.getExecuteTime()),
							JSON.toJSONString(loanRepayDetailVO).getBytes("UTF-8")));

				}else if(exception instanceof LoanRepaySyncException){
					logger.info("申请流水号 : 【" + applySeq + "还款同步服务执行失败,转为定时任务中执行");
					methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.SUCCESS.getCode());
					nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.SUCCESS.getCode());

					//设置插槽结果信息
					loanRepayDetailVO.setSoltResultDetails(JSONObject.toJSONString(nlsProcessBizVo));
					//设置规则结果信息
					loanRepayDetailVO.setRuleResultDetails(JSONObject.toJSONString(nlsProcessRuleVO));
					//设置结果码
					loanRepayDetailVO.setRspCode(NlsProcessBizEnums.RspInfoEnum.SUCCESS.getRspCode());
					//设置结果信息
					loanRepayDetailVO.setRspMsg(NlsProcessBizEnums.RspInfoEnum.SUCCESS.getRspMsg());
					//设置审批状态信息为处理中
					loanRepayDetailVO.setWfApprSts(NlsApplyInfoEnums.NlsApplyState.DEALING.getKey());
					// 设置还款状态
					loanRepayDetailVO.setRpSts(RY_STS_1);
					// 设置流程状态
					loanRepayDetailVO.setFlowStatus(FLOW_STATUS_5);

				} else{
					logger.info("申请流水号 : 【" + applySeq + "处理该插槽的其它异常开始，更新该插槽状态为失败， 更新网贷任务队列中队列任务状态为执行失败和更新还款申请审批状态为拒绝");
					methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.FAILURE.getCode());
					nlsProcessBizVo.setHangTimes(hangTimes);
					nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.FAILURE.getCode());
					//设置插槽结果信息 
					loanRepayDetailVO.setSoltResultDetails(JSONObject.toJSONString(nlsProcessBizVo));
					//设置规则结果信息 
					loanRepayDetailVO.setRuleResultDetails(JSONObject.toJSONString(nlsProcessRuleVO));
					//设置结果码 
					loanRepayDetailVO.setRspCode(NlsProcessBizEnums.RspInfoEnum.FAILURE.getRspCode());
					//设置结果信息 
					loanRepayDetailVO.setRspMsg(NlsProcessBizEnums.RspInfoEnum.FAILURE.getRspMsg());
					//设置审批状态信息为失败 
					loanRepayDetailVO.setWfApprSts(NlsApplyInfoEnums.NlsApplyState.REJECT.getKey());
					//设置拒绝原因 
					loanRepayDetailVO.setRefuseCause(exception.getMessage());
					logger.info("申请流水号 : 【" + applySeq + "处理该插槽的其它异常结束");
				}
				//最近修改时间 
				nlsProcessBizVo.setLastModifyTime(DateUtility.getCurrAppDateTimeString());
				loanRepayDetailVO.setLastChgDt(DateUtility.getCurrAppDateTimeString());

				nlsProcessBizService.updateByPk(nlsProcessBizVo);

				/**
				 * mycat中不允许修改作为分库依据的列，所以更新的时候去掉“loan_no”
				 * 将更新执行的bean中loanNo字段赋值为null，更新语句会检测是否为空，再做更新。
				 * @date 2019/06/15
				 * @author qiuyf
				 * 
				 * 【2019/06/17，需求要求不使用mycat分片】
				 * loanRepayDetailVO.setLoanNo(null);
				 */
				loanRepayDetailService.updateByPk(loanRepayDetailVO);

				throw exception;
			} else {
				logger.info("申请流水号 : 【" + applySeq + "该插槽的无异常开始，更新该插槽状态为成功，此处队列任务表和还款明细表的数据需要等插槽全部执行完成后才更新");
				// 更新该插槽状态为成功
				methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.SUCCESS.getCode());
				nlsProcessBizVo.setHangTimes(QueueTaskEnums.QueueHangTimesEnum.INITIALIZE.getTimes());
				// 此处队列任务表和还款明细表的数据需要等插槽全部执行完成后才更新
				logger.info("申请流水号 : 【" + applySeq + "该插槽的无异常结束");
			}
			// 最近修改时间
			nlsProcessBizVo.setLastModifyTime(DateUtility.getCurrAppDateTimeString());
			nlsProcessBizService.updateByPk(nlsProcessBizVo);
		} catch (ExternalSysException e) {
			logger.error("队列插槽服务中申请流水号:【" + applySeq + "】的处理外部系统异常信息为：" + e.getMessage());
			throw new ExternalSysException();
		} catch (BizException e) {
			logger.error("队列插槽服务中申请流水号:【" + applySeq + "】的业务异常信息为：" + e.getMessage());
			throw new BizException();
		} catch (Exception e) {
			logger.error("队列插槽服务中申请流水号:【" + applySeq + "】的其他异常信息为：" + e.getMessage());
			throw new Exception();
		}

	}

	/**
	 * 供插槽实现的抽象方法
	 */
	protected abstract void doExecute(NlsProcessBizVO nlsProcessBizVo, LoanRepayDetailVO loanRepayDetailVO) throws Exception;

	@Autowired
	@Qualifier("nlsProcessBizService")
	private NlsProcessBizService nlsProcessBizService;
	@Autowired
	@Qualifier("loanRepayDetailService")
	private LoanRepayDetailService loanRepayDetailService;
	@Autowired
	@Qualifier("nlsProcessRuleService")
	private NlsProcessRuleService nlsProcessRuleService;
	@Autowired
	@Qualifier("producerService")
	private MQProducerMessageClient producerService;
	@Autowired
	@Qualifier("nlsQueueSoltComnService")
	private NlsQueueSoltComnService nlsQueueSoltComnService;
	

}
